#include "HorizontalSplitableWidget.h"

#include "Widgets/RectangleWidget.h"
#include "Widgets/Mouse/MouseWidget.h"
#include "Widgets/Positioners/HorizontalPositionerWidget.h"

RHorizontalSplitableWidget::RHorizontalSplitableWidget()
{
    SlotContainer= NewWidget<RHorizontalPositionerWidget>(this);
    SlotContainer->Anchor.Fill(this);


    SpliterContainer= NewWidget<RWidget>(this);
    SpliterContainer->Anchor.Fill(this);
}

RWidgetPtr RHorizontalSplitableWidget::AddSlot(float SizeWeight)
{
    auto SlotWidget= NewWidget<RWidget>(SlotContainer);
    SlotWidget->Anchor.VerticalFill(SlotContainer);
    
    SSlot& Slot=Slots.Emplace();
    Slot.Widget=SlotWidget;
    Slot.SizeWeight=SizeWeight;
    
    if(Slots.Num()>1)
    {
        auto Spliter= NewWidget<RMouseWidget>(SpliterContainer);
        Slot.FormerSpliter=Spliter;

        //store the index of this slot
        Spliter->Tags->UpdateTag(U"LaterSlotIndex",(int64_t)Slots.Num()-1);

        Spliter->Anchor.VerticalFill(this);
        Spliter->CursorShape=EMouseCursorType::ResizeHorizontal;

        auto Background= NewWidget<RRectangleWidget>(Spliter);
        Background->Anchor.Fill(Spliter);
        Background->FillColor=SColor::FromHexRGB(0x333333);

        TWeakObjectPtr<RRectangleWidget> WeakBackground=Background; 
        Spliter->OnEntered.BindLambda([this,WeakBackground]()->bool
        {
            WeakBackground->FillColor=SColor::FromHexRGB(0x555555);
            return false;
        });
        
        Spliter->OnExited.BindLambda([this,WeakBackground]()->bool
        {
            WeakBackground->FillColor=SColor::FromHexRGB(0x333333);
            return false;
        });

        Spliter->Drag->bIsEnabled=true;
        Spliter->Drag->Target=(Spliter);

        
        TWeakObjectPtr<RMouseWidget> WeakSpliter=Spliter;
        Spliter->Drag->OnDragStarted.Bind([this,WeakSpliter]()->void
        {
            StartDragX=WeakSpliter->X.Get();
            
            for(auto& Slot:Slots)
            {
                StartDragTotalSizeWeight+=Slot.SizeWeight;
            }

            int64_t LaterSlotIndex=AnyCast<int64_t>(WeakSpliter->Tags->GetTag(U"LaterSlotIndex"));

            StartDragFormerSizeWeight=Slots[LaterSlotIndex-1].SizeWeight;
            StartDragLatterSizeWeight=Slots[LaterSlotIndex].SizeWeight;

            StartDragFormerLatterSize=Slots[LaterSlotIndex-1].Widget->Width.Get()+Slots[LaterSlotIndex].Widget->Width.Get();
        });


        Spliter->Drag->OnDragMoved.Bind([this,WeakSpliter](const SVec2& DragDistance)->void
        {
            auto WeightPerSize=(StartDragFormerSizeWeight+StartDragLatterSizeWeight)/ StartDragFormerLatterSize;
            auto Delta=DragDistance.X* WeightPerSize;

            int64_t LaterSlotIndex=AnyCast<int64_t>(WeakSpliter->Tags->GetTag(U"LaterSlotIndex"));

            Slots[LaterSlotIndex-1].SizeWeight=StartDragFormerSizeWeight+Delta;
            Slots[LaterSlotIndex].SizeWeight=StartDragLatterSizeWeight-Delta;
        });

        Spliter->Drag->Direction=EDragDirection::Horizontal;

        Spliter->Drag->TargetWidgetMinX=[this,WeakSpliter]()->float
        {
            int64_t LaterSlotIndex=AnyCast<int64_t>(WeakSpliter->Tags->GetTag(U"LaterSlotIndex"));
            return Slots[LaterSlotIndex-1].Widget->GetPresentAbsolutePosition().X+2;
        };

        Spliter->Drag->TargetWidgetMaxX=[this,WeakSpliter]()->float
        {
            int64_t LaterSlotIndex=AnyCast<int64_t>(WeakSpliter->Tags->GetTag(U"LaterSlotIndex"));
            return Slots[LaterSlotIndex].Widget->GetPresentAbsolutePosition().X+Slots[LaterSlotIndex].Widget->GetPresentSize().X-2;
        };

    }
    return SlotWidget;
}

RWidgetPtr RHorizontalSplitableWidget::GetSlot(int32_t Index)
{
    assert(Index>=0&&Index<Slots.Num());
    return Slots[Index].Widget;
}

void RHorizontalSplitableWidget::RemoveSlot(int32_t Index)
{
    assert(Index>=0&&Index<Slots.Num());
    Slots[Index].Widget->RemoveFromParent();
    if(Slots[Index].FormerSpliter)
    {
        Slots[Index].FormerSpliter->RemoveFromParent();
    }

    if((Index+1)<Slots.Num())
    {
        Slots[Index+1].FormerSpliter->Tags->UpdateTag(U"LaterSlotIndex",(int64_t)Index);
    }

    Slots.RemoveAt(Index);
}

void RHorizontalSplitableWidget::CalculateChildrenPresentPositionAndSize()
{
    float _Spaceing=Spacing.Get();
    SlotContainer->Spacing=_Spaceing;
    float TotalSizeWeight=0;
    for(auto& Slot:Slots)
    {
        TotalSizeWeight+=Slot.SizeWeight;
    }
    float TotalSpacing=_Spaceing*(Slots.Num()-1);
    float TotalLength= PresentSize.X-TotalSpacing;
    float CurrentX=0;
    for(auto& Slot:Slots)
    {
        if(Slot.FormerSpliter)
        {
            Slot.FormerSpliter->X=CurrentX;
            Slot.FormerSpliter->Width=_Spaceing;
            CurrentX+=_Spaceing;
        }

        float Length=TotalLength*Slot.SizeWeight/TotalSizeWeight;
        Slot.Widget->X=CurrentX;
        Slot.Widget->Width=Length;
        CurrentX+=Length;

    }

    SlotContainer->CalculatePresentPositionAndSize();
    SpliterContainer->CalculatePresentPositionAndSize();
}
